﻿using System;
using System.Runtime.InteropServices;
using System.Text;
using SHDocVw;

namespace Ooganizer.Resolver
{

    public class InternetExplorerResolver : IResolver, IResolveable
    {

        // .net implementation of EnumChildProc Callback        
        public delegate bool EnumChildProc(IntPtr hWnd, int lParam);

        [DllImport("User32.dll")]
        extern public static bool EnumChildWindows(IntPtr hWndParent, Delegate lpEnumFunc, int lParam);

        [DllImport("User32.dll")]
        extern public static int GetClassName(IntPtr hWnd, [Out] StringBuilder lpClassName, int nMaxCount);

        [DllImport("User32.dll")]
        extern public static bool IsWindowVisible(IntPtr hWnd);

        [DllImport("User32.dll")]
        extern public static int GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, int nMaxCount);

        public bool CanResolve(string processName)
        {
            switch (processName.ToLower())
            {
                case "iexplore":
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Main GetArtifact function responsible for resolving the window
        /// </summary>
        /// <param name="hWnd">handle to window</param>
        /// <param name="artifact">artifact is passed back out</param>
        /// <returns></returns>
        public ArtifactGroup GetArtifacts(IntPtr hWnd)
        {
            // For Testing Purpose, enter an hWnd here to resolve
            // hWnd = (IntPtr) 0x00670750;

            string strTabText = GetActiveTabWindowText(hWnd);
            bool bIsIELegacy = (strTabText == "");

            IShellWindows pIShellWindows = new ShellWindows();
            if (pIShellWindows == null)
                throw (new Exception("Shell Window Instantiation Failed"));

            ArtifactGroup artifacts = new ArtifactGroup();

            // Iterate thru each shell/browser window trying to match it correctly
            // to the one we want to get an artifact for
            for (int i = 0; i < pIShellWindows.Count; ++i)
            {
             
                IWebBrowser2 webBrowser = pIShellWindows.Item(i) as IWebBrowser2;

                if (webBrowser != null)
                {
                    if (bIsIELegacy)
                    {
                        if (hWnd == (IntPtr)webBrowser.HWND)
                        {
                            Artifact artifact = new Artifact();
                            artifact.Name = webBrowser.LocationName;
                            artifact.Location = webBrowser.LocationURL;
                            artifacts.AddPrimary(artifact);
                        }
                    }
                    else
                    {
                        ////
                        // Non-Legacy we add all the tabs for each window,
                        // we make the active tab the primary artifact
                        ////
                        if (hWnd == (IntPtr)webBrowser.HWND)
                        {
                            Artifact artifact = new Artifact();
                            artifact.Name = webBrowser.LocationName;
                            artifact.Location = webBrowser.LocationURL;

                            if(Helper_IE7_WindowCmpIsEqual(strTabText, webBrowser.LocationName))   
                                artifacts.AddPrimary(artifact);
                            else 
                                artifacts.Add(artifact);
                            
                        }
                    }
                }
            }
            
            return artifacts;
        }
                
        /// <summary>
        /// The Window text we are getting is not exactly the same as the TabText of the BrowserLocation,
        /// we have to check the smaller of the two (and trim them) 
        /// and make the comparison that way, to be as accurate as we possibly can
        /// </summary>
        /// <param name="strTabText"></param>
        /// <param name="strLocationName"></param>
        /// <returns></returns>
        private bool Helper_IE7_WindowCmpIsEqual(string strTabText, string strLocationName)
        {

            strTabText = strTabText.Trim();
            strLocationName = strLocationName.Trim();

            int nOurMaxToCmp = Math.Min(strTabText.Length, strLocationName.Length);

            if (nOurMaxToCmp >= 1)
            {
                return (String.Compare(strTabText, 0, strLocationName, 0, nOurMaxToCmp, true) == 0);
            }
            else
                return false;
        }

        // We need to store the Active Text in a static var
        private static string g_strActiveTabText;
        
        /// <summary>
        /// We are looking for Internet Explorer's Tab classes, if we find it we set g_strActiveTabText to
        /// not ""
        /// </summary>
        /// <param name="hWnd">handle to a Window</param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        public static bool EnumChildProcImpl(IntPtr hWnd, int lParam)
        {
            StringBuilder sb = new StringBuilder();
            GetClassName(hWnd, sb, sb.MaxCapacity);

            if (sb.ToString().ToLower().Contains("tabwindowclass") &&
                IsWindowVisible(hWnd))
            {
                // we found the active Tab, so pass it out vipa static string
                StringBuilder sb2 = new StringBuilder();
                if (GetWindowText(hWnd, sb2, sb2.MaxCapacity) > 0)
                    g_strActiveTabText = sb2.ToString();
                return false;
            }
            else
            {
                g_strActiveTabText = ""; // reset it, everytime it is not found
                return true;
            }
        }
        
        /// <summary>
        /// Function will iterate thru the Parent Internet Explorer Window and Return the window text
        /// of the active Tab or "" 
        /// ~returning "" means that this is not Internet Explorer 7 or higher
        /// </summary>
        /// <param name="hWnd">handle to a Window</param>
        /// <returns></returns>
        private string GetActiveTabWindowText(IntPtr hWnd)
        {
            // reset static variable just to make sure, we don't get any false positives
            g_strActiveTabText = "";

            // Iterate thru all the tabs, and 
            EnumChildProc proc = new EnumChildProc(EnumChildProcImpl);
            EnumChildWindows(hWnd, proc, 0);

            // Stripping out the last " - " from the Window Text of Internet Explorer,
            // usually this text contains "Windows Internet Explorer" 
            if (g_strActiveTabText != "")
            {
                int index = g_strActiveTabText.LastIndexOf(" - ");
                g_strActiveTabText = g_strActiveTabText.Substring(0, index);
            }

            // Variable now contains either the active tab window text or ""
            return (g_strActiveTabText);
        }
    }
}
